Prerequisites

Loading the required packages

library(tidyverse)
library(dplyr)
library(ggplot2)
library(scales)

Dataset

Import processed data, which can be found here.

#read preprocessed data
wines <- read.csv(file = '../data/processed_data/wines.csv')

Get sample of dataset

#set seed value to birthday of Ricardo Rodriguez, American wrestler and ring announcer and Dr. Reinaldo (Rei) Sanchez-Arias
set.seed(19630217)

#set percentage to test with for simplicity, if needed
percentage <- 5
wine_sample<- sample_n(wines, percentage/100*nrow(wines))

Rating Classification

Wines are normally classified in categories as found on the website. To create a more rich dataset we added the field rating_category determined as:

Category Rating Description
Classic 98-100 The pinnacle of quality.
Superb 94-97 A great achievement.
Excellent 90-93 Highly recommended.
Very Good 87-89 Often good value; well recommended.
Good 83-86 Suitable for everyday consumption; often good value.
Acceptable 80-82 Can be employed in casual, less-critical circumstances

Also, this allows to take a quantitative data and turn it into qualitative data, which can be used for data visualization later.

# function to add rating
rating_category <- function(points){
  if(points>=98){
    return("Classic")
  }
  else if (points>=94){
    return("Superb")
  }
  else if(points>=90){
    return("Excellent")
  }
  else if(points>=87){
    return("Very Good")
  }
  else if(points>=83){
    return("Good")
  }
  else{
    return("Acceptable")
  }
}

wines<- wines %>%
  rowwise() %>%
  mutate(rating_category = rating_category(points))
head(wines)

Create a Reviewer Profile

Split Taster Data

To make our dataframes more managable we split reduntant information about the tasters into a new dataframe.

tasters <- wines %>%
  select(taster_name, taster_twitter_handle) %>% 
  unique()
tasters

Drop taster_twitter_handle in wines dataframe

wines <- wines %>%
  select(-taster_twitter_handle)
head(wines)

Create Reviewer Metrics

Each reviewer has there own bias. In order to offset that we made a “profile” for each reviewer, This profile allows us to later normalize the wine points for more robust apples to apples comparison. Each reviewers profile includes additional characteristics like:

  • avg_points which is the avgerage of all the reviewer’s scores

  • sd_points which is the standard deviation of all the reviewer’s scores

  • var_points which is the variance of all the reviewer’s scores

  • reviews which is the number of reviews conducted

taster_rating_profile <- wines %>%
  group_by(taster_name) %>%
  summarize(
    avg_points = mean(points),
    sd_points = sd(points),
    var_points = var(points),
    reviews = n()
  )
Grouping rowwise data frame strips rowwise nature
tasters <- inner_join(tasters, taster_rating_profile, by = "taster_name")
head(tasters)

Normalized Points

Since, each reviewer has a different bias we created a normalized metric, norm_points, by looking at the number of standard deviatioins a wine is from the reviewer’s avg_points. This gives use a more accurate representation of which which wines are “better” than the rest.

normalize_points <- function(data){
  left_join(data, tasters, by = "taster_name")%>%
    rowwise() %>%
    mutate(norm_points = (points-avg_points)/sd_points) %>%
    select(-avg_points, -sd_points, -var_points, -taster_twitter_handle, -reviews)
}

wines <- normalize_points(wines)
head(wines) 

Data Sanitation

Here we do hard checks to clean data to ensure integrity of our data.

Vintage seems to have year 7200, so we filtered all data up to 2019

wines <- wines %>%
  filter(vintage<2019)

Data Exploration

Before, conducting any detailed analysis of our dataset, we looked at a quick summary of the dataset

summary(wines)
                                                                            title       
 Château Lestage Simon 2012  Haut-Médoc                                        :     4  
 Château Maine-Gazin 1997  Bordeaux                                            :     4  
 Concannon 2009 Selected Vineyards, Central Coast Pinot Noir (Livermore Valley):     4  
 Domaine Vacheron 2015  Sancerre                                               :     4  
 Vignerons des Pierres Dorées 2015 Salamandre d'Or  (Coteaux Bourguignons)     :     4  
 Adega Cooperativa de Borba 2009 Senses Syrah (Alentejano)                     :     3  
 (Other)                                                                       :117855  
    alcohol               category        vintage           designation   
 Min.   :   2.20   Dessert    : 1648   Min.   :1150               :35285  
 1st Qu.:  13.00   Fortified  :   60   1st Qu.:2009   Reserve     : 1924  
 Median :  13.53   Port/Sherry:  386   Median :2011   Estate      : 1236  
 Mean   :  13.84   Red        :72354   Mean   :2010   Reserva     : 1214  
 3rd Qu.:  14.40   Rose       : 4203   3rd Qu.:2013   Riserva     :  669  
 Max.   :8333.00   Sparkling  : 2273   Max.   :2017   Estate Grown:  584  
 NA's   :9060      White      :36954                  (Other)     :76966  
     country                province                      region     
 US      :50228   California    :33418                       :19360  
 France  :19164   Washington    : 8106   Napa Valley         : 4234  
 Italy   :17497   Bordeaux      : 5665   Columbia Valley (WA): 3855  
 Spain   : 5890   Tuscany       : 5647   Russian River Valley: 2896  
 Portugal: 4905   Oregon        : 4824   California          : 2200  
 Chile   : 4235   Northern Spain: 3712   Paso Robles         : 2193  
 (Other) :15959   (Other)       :56506   (Other)             :83140  
             subregion                         variety     
                  :70775   Pinot Noir              :12071  
 Central Coast    :10277   Chardonnay              :10813  
 Sonoma           : 8527   Cabernet Sauvignon      : 8933  
 Columbia Valley  : 7617   Red Blend               : 8233  
 Napa             : 6450   Bordeaux-style Red Blend: 6564  
 Willamette Valley: 3286   Riesling                : 4893  
 (Other)          :10946   (Other)                 :66371  
                   winery           price             points      
 DFJ Vinhos           :   208   Min.   :   4.00   Min.   : 80.00  
 Williams Selyem      :   207   1st Qu.:  17.00   1st Qu.: 86.00  
 Testarossa           :   205   Median :  25.00   Median : 88.00  
 Louis Latour         :   193   Mean   :  35.48   Mean   : 88.47  
 Chateau Ste. Michelle:   192   3rd Qu.:  42.00   3rd Qu.: 91.00  
 Georges Duboeuf      :   192   Max.   :3300.00   Max.   :100.00  
 (Other)              :116681   NA's   :7974                      
            taster_name    rating_category     norm_points      
                  :23945   Length:117878      Min.   :-4.49580  
 Roger Voss       :22265   Class :character   1st Qu.:-0.72711  
 Michael Schachner:13867   Mode  :character   Median : 0.03980  
 Kerin O’Keefe    : 9747                      Mean   : 0.01183  
 Virginie Boone   : 8926                      3rd Qu.: 0.70027  
 Paul Gregutt     : 8747                      Max.   : 4.46378  
 (Other)          :30381                                        

Univariate Exploration

To better understand the distribution of our data we did some simple univariate visualization based on certain fields. Additionally, before doing a multivariate analysis and answering our research questions we first want to ensure our dataset is robust and an accurate representation of the real world.

Alcohol Amount

The visualization below depicts the distribution of our dataset based on alcohol percentage, alcohol . To better understand and visualize the data we categorized the graph based on rating_category. Notice, a majority of wines have an alcohol amount between 12%-15% and according to Real Simple wine alcholic content averages between 11%-13%. This leads us to believe our data is an accurate representation of the real world.

wines %>% 
  group_by(alcohol) %>% 
  ggplot() +
  geom_histogram(
    mapping = aes(
      x = alcohol, 
      fill = rating_category),
    na.rm = TRUE,
    bins = 50) +
  scale_x_continuous(
    breaks = seq(0,25,1), 
    limits = c(4,22)) +
  labs(
    title = "Distribution of Alcohol Percentage",
    x = "Alcohol Percentage",
    y = "Count",
    fill = "Rating Category"
  )
Grouping rowwise data frame strips rowwise nature

Vintage

Next, we wanted to see what vintage most of the wines in the dataset were. Again to better understand and visualize the data we categorized the graph based on rating_category. Notice, that there is roughly, an equal percentage of each rating category per vintage.

(Note: Data points before 1990 have been omitted for clarity in visualization)

wines %>%
  ggplot() +
  geom_bar(
    mapping = aes(
      x=vintage, 
      fill = rating_category),
    na.rm = TRUE) +
  scale_x_continuous(
    breaks = seq(1990,2019,5), 
    limits = c(1990,2019)) +
  labs(
    title = "Distribution of Vintage",
    x = "Vintage", 
    y = "Count",
    fill = "Rating Category")

Winery

To better understand the number wines per winery, we did a visualization that counts the number of wines per winery showing only Top 10 winerys to give you an idea what winery has the most selction of wines. Notice, each of the top 10 producers of wine have over 100 different wine labels.

wines %>%
  group_by(winery) %>%
  summarise(count = n()) %>%
  arrange(desc(count)) %>%
  slice(1:10) %>%
  ggplot() +
  geom_col(
    mapping = aes(
      x= reorder(winery, count),
      y = count,
      fill = winery)) +
  labs(
    title = "Distribution of Winery (Top 10)",
    x = "Winery", 
    y = "Count") +
  theme(legend.position = "none") +
  coord_flip()
Grouping rowwise data frame strips rowwise nature

Province

To better understand the number wines per province, we did a visualization that counts the number of wines per province showing only the top 10 provinces with the most wines. This can give the reader an idea where their wine will most likely be made with California standing out as a clear leader.

wines %>% 
  group_by(province) %>% 
  summarize(count = n()) %>% 
  arrange(desc(count)) %>% 
  slice(1:10) %>% 
  ggplot()+
  geom_col(
    mapping = aes(
      x = reorder(province, count), 
      y = count,
      fill = province)) +
  labs(
    title = "Distribution of Province (Top 10)",
    x = "Province", 
    y = "Count") +
  theme(legend.position = "none") +
  coord_flip()
Grouping rowwise data frame strips rowwise nature

Wine Category

Next, we wanted to visualize th distribution of different wine categories in our dataset.To better understand and visualize the data we categorized the graph based on rating_category. Notice, a majority of the wines are red or white wines

wines %>% 
  ggplot()+
  geom_bar(
    mapping = aes(
      x = category,
      fill = rating_category)) +
  labs(
    title = "Distribution of Wine Category",
    x = "Wine Category", 
    y = "Count",
    fill = "Rating Category") +
  coord_flip()

Price

Next, we wanted to visualize the distribution of price in our dataset. To better understand and visualize the data we categorized the graph based on rating_category. Notice, a majority of wines are $50 and below with the most common being between $12 - $25. Again, this accurately represents the real world as stated by Vivino the average price for good bottle of red/white wine is ~$15 and ~$28 for a very good bottle. (CAUTION: The Vivino prices denoted were simply an average for red/white wines average costs. This was done to simply generalize the information to do a simple comparsion. Also, this limited to red/white wine and does not accurately include other types)

(Note: Data points above $400 have been omitted for clarity in visualization)

wines %>% 
  filter(price < 400) %>% 
  ggplot() +
  geom_histogram(
    mapping = aes(
      x=price, 
      fill = rating_category),
    binwidth = 15) +
  labs(
    title = "Distribution of Price",
    x = "Price", 
    y = "Count",
    fill = "Rating Category")

Points

Next, we wanted to visualize the distribution of points in our dataset. Notice, here that a majority of wines recieve a score between 87 and 90. Which is accurate to the information provided on Wine Searcher which states 50% of the scores fall between 86-90 point from Wine Enthusiast ratings.

(Note: We our dataset was reterived from the Wine Enthusiast website)

wines %>%
  ggplot() +
  geom_histogram(
    mapping = aes(x=points),
    bins = 20)+
  labs(
    title = "Distribution of Points",
    x = "Points", 
    y = "Count")

Multivariate Exploration

Now, that we have a better understanding of our data and we know it is a accuare representation of the real world we can preform an more detailed anaylsis using multiple variables.

Points by Taster

To understand the point distribution by tasters, we did a multivarite visualization that coorelates taster names based on the average wine points as identified by the x-intercept. This gives the reader an idea of how some reviewers correlate to the overall average.

(Note: The “blank” represents unknown reviewers. We assumed the reviewers not named have not rated a signficant amount of wines and can be grouped into a singular reviewer)

wines %>%
  ggplot() +
  geom_boxplot(
    mapping = aes(
      x=taster_name,
      y=points, 
      color = taster_name)) +
  geom_hline(yintercept = mean(wines$points)) +
  theme(legend.position = "none")+
  labs(
    title = "Points by Taster",
    x="Taster Name",
    y="Points"
  )+
  coord_flip()

Price by Points

To understand the price distribution by points, we did a multivarite visualization that creates a scatter plot of the wines based on points and price. Then, we added a smooth transformation on identify trends. Notice, the data is “stacked” and the socres range from 80-100

wines %>% 
  ggplot() +
  geom_point(
    mapping = aes(
      x = points, 
      y = price, 
      color = category),
    na.rm = TRUE,
    alpha = 0.2) +
  labs(
    title = "Price by Points", 
    x = "Points",
    y = "Price",
    color = "Wine Category") +
  geom_smooth(
    mapping = aes(
      x = points, 
      y = price),
    na.rm = TRUE)

Since, there are multiple outliers and the visualization is clustered. By preforming a log on all the prices we can reduce the skewness of the visualization. Notice, as quality of wine increases price increases exponentially.

wines %>% 
  ggplot() +
  geom_point(
    mapping = aes(
      x = points, 
      y = log(price), 
      color = category),
    na.rm = TRUE,
    alpha = 0.2) +
  labs(
    title = "log(Price) by Points", 
    x = "Points",
    y = "log(Price)",
    color = "Wine Category") +
  geom_smooth(
    mapping = aes(
      x = points, 
      y = log(price)),
    na.rm = TRUE)

Group by Wine Category

Next, our group wanted to do a more granular analysis by looking at how the price varies by points grouped by wine category. Notice, all the price go up as points go up, but the growth rates are different per wine category.

wines %>% 
  ggplot() +
  geom_point(
    mapping = aes(
      x = points, 
      y = log(price), 
      color = category),
    alpha =0.2,
    na.rm = TRUE) +
  geom_smooth(
    mapping = aes(
      x = points, 
      y = log(price)),
    na.rm = TRUE) +
  facet_wrap(~category) +
  labs(
    title = "log(Price) by Points", 
    x = "Points",
    y = "log(Price)")+
  theme_minimal()+
  theme(legend.position = "none" )

Data Analysis

Now that we fully understand the dataset we are working with. We plan to answer some research questions proposed by the team.

What is the best wine?

A easy way to determine the best wine is by simply finding the top 10 wines.

wines %>%
  arrange(desc(points)) %>%
  slice(1:10) %>%
  select(title,price, points,rating_category, norm_points)

However, this does not account for the tasters bias. Instead, our group “normalized” the points based on each taster based on the number of standard deviations an wines is from the raters average. For example, Taster A could give a wine 100 but has an avgerage rating score of 95 with a standard deviation of 5. Whereas, Taster B could give a wine 91 and have an average score of 87 with a standard deviation of 2. Although, the wine tasted by Taster A got a perfect 100 score, Taster B’s wine was much “better” wine since it was 2 standard deviations from the tasters avgerage compared to 1 standard deviation of the other wine.

Looking at the norm_points these are the top 10 best wines

wines %>%
  arrange(desc(norm_points)) %>%
  slice(1:10)%>%
  select(title,price, points,rating_category, norm_points)

What is the best value wine?

A simple value metric we can use to determine best value is points/price

wines %>%
  arrange(desc(points/price)) %>%
  slice(1:10)%>%
  select(title,price, points,rating_category, norm_points)

However, again this metric is not normalized. Instead, norm_points/price would yield more robust results.

wines %>%
  arrange(desc(norm_points/price)) %>%
  slice(1:10) %>%
  select(title,price, points,rating_category, norm_points)

Which province has the best wine?

To determine the best province for wine by points, we averaged the points of all wines per province with a sample size greater than 30 and returned the top 10 with standard error. Notice, how the standard error is low menaing the spread of our data is also low and the average is very accurate.

wines %>% 
  group_by(province) %>%
  summarise(
    avg_points_prov = mean(points), 
    count = n(), 
    std_err_points_prov = sd(points)/sqrt(count)) %>%
  filter(count>30) %>%
  arrange(desc(avg_points_prov)) %>%
  slice(1:10) %>%
  ggplot() +
  geom_col(
    mapping = aes(
      x = reorder(province,avg_points_prov), 
      y = avg_points_prov,
      fill = province)) +
   geom_errorbar(
    mapping = aes(
      x = province,
      y = avg_points_prov,
      ymin = avg_points_prov - std_err_points_prov, 
      ymax = avg_points_prov + std_err_points_prov
      ),
    width = 0.2)+
  scale_y_continuous(
    limits=c(85,95), 
    oob = rescale_none)+
  labs(
      x = 'Province', 
      y = "Average Points", 
      title = "Average Points By Province (Top 10)") +
  theme(legend.position = "none")+
  coord_flip()
Grouping rowwise data frame strips rowwise nature

Which wine variety is the best?

To determine the best variety of wine we use the average point of all wines per variety with a sample size greater than 30. The graph below shows the the top 10 varieties with their respective standard error.

wines %>% 
  group_by(variety) %>%
  summarise(
    avg_points_variety = mean(points), 
    count = n(), 
    std_err_points_variety = sd(points)/sqrt(count)) %>%
  filter(count>30) %>%
  arrange(desc(avg_points_variety)) %>%
  slice(1:10) %>%
  ggplot() +
  geom_col(
    mapping = aes(
      x = reorder(variety,avg_points_variety), 
      y = avg_points_variety,
      fill = variety)) +
   geom_errorbar(
    mapping = aes(
      x = variety,
      y = avg_points_variety,
      ymin = avg_points_variety - std_err_points_variety, 
      ymax = avg_points_variety + std_err_points_variety
      ),
    width = 0.2)+
  scale_y_continuous(
    limits=c(85,95), 
    oob = rescale_none)+
  labs(
      x = 'Variety', 
      y = "Average Points", 
      title = "Average Points By Variety (Top 10)") +
  theme(legend.position = "none")+
  coord_flip()
Grouping rowwise data frame strips rowwise nature

Jess from New Girl’s favorite wine?

Sounds like a silly question, but take a closer look and you’ll find an interesting question within it: “Can we determine the best value wine based on how much people are willing to pay?” WE SURE CAN!

# If you want to get user input
#user_price <- readline(prompt = "How much are you willing to spend on a bottle?")
#user_price <- as.integer(user_price)
user_price<-11

wines %>% 
  filter(price <= user_price) %>%
  arrange(desc(norm_points/price)) %>%
  slice(1:10) %>%
  select(title, price, points,rating_category, norm_points)
Now back to the orignal question with Jess
wines %>% 
  filter(price < 11) %>%
  filter(category == "Sparkling") %>%
  filter(grepl("pink", title, ignore.case = T) == T) %>%
  select(title,price, points,rating_category, norm_points)

As you can see, “Yellow Tail 2015 Pink Bubbles Sparkling (South Australia)” is Jess’s type of wine!

Conclusion

Future Works

Works Cited

LS0tCnRpdGxlOiAiRXhwbG9yaW5nIGFuZCBBbmFseWl6aW5nIFdpbmUgRW50aHVzaWFzdCBSZXZpZXdzIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdGhlbWU6IHVuaXRlZAotLS0KCiMgUHJlcmVxdWlzaXRlcwoKTG9hZGluZyB0aGUgcmVxdWlyZWQgcGFja2FnZXMKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoc2NhbGVzKQpgYGAKCiMgRGF0YXNldAoKSW1wb3J0IHByb2Nlc3NlZCBkYXRhLCB3aGljaCBjYW4gYmUgZm91bmQgW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9DNHJieW4zbTRuL3dpbmVfcmV2aWV3c19kYXRhX2FuYWx5c2lzL2Jsb2IvbWFzdGVyL2RhdGEvcHJvY2Vzc2VkX2RhdGEvcHJlcHJvY2Vzc2luZy5ybWQpLgoKYGBge3J9CiNyZWFkIHByZXByb2Nlc3NlZCBkYXRhCndpbmVzIDwtIHJlYWQuY3N2KGZpbGUgPSAnLi4vZGF0YS9wcm9jZXNzZWRfZGF0YS93aW5lcy5jc3YnKQpgYGAKCkdldCBzYW1wbGUgb2YgZGF0YXNldApgYGB7cn0KI3NldCBzZWVkIHZhbHVlIHRvIGJpcnRoZGF5IG9mIFJpY2FyZG8gUm9kcmlndWV6LCBBbWVyaWNhbiB3cmVzdGxlciBhbmQgcmluZyBhbm5vdW5jZXIgYW5kIERyLiBSZWluYWxkbyAoUmVpKSBTYW5jaGV6LUFyaWFzCnNldC5zZWVkKDE5NjMwMjE3KQoKI3NldCBwZXJjZW50YWdlIHRvIHRlc3Qgd2l0aCBmb3Igc2ltcGxpY2l0eSwgaWYgbmVlZGVkCnBlcmNlbnRhZ2UgPC0gNQp3aW5lX3NhbXBsZTwtIHNhbXBsZV9uKHdpbmVzLCBwZXJjZW50YWdlLzEwMCpucm93KHdpbmVzKSkKYGBgCgojIyBSYXRpbmcgQ2xhc3NpZmljYXRpb24KCldpbmVzIGFyZSBub3JtYWxseSBjbGFzc2lmaWVkIGluIGNhdGVnb3JpZXMgYXMgZm91bmQgb24gdGhlIFt3ZWJzaXRlXShodHRwczovL3d3dy53aW5lbWFnLmNvbS8yMDEwLzA0LzA5L3lvdS1hc2tlZC1ob3ctaXMtYS13aW5lcy1zY29yZS1kZXRlcm1pbmVkLykuIFRvIGNyZWF0ZSBhIG1vcmUgcmljaCBkYXRhc2V0IHdlIGFkZGVkIHRoZSBmaWVsZCBgcmF0aW5nX2NhdGVnb3J5YCBkZXRlcm1pbmVkIGFzOgoKfENhdGVnb3J5ICB8IFJhdGluZyAgfCBEZXNjcmlwdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8LS0tLS0tLS0tLXwtLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18CnxDbGFzc2ljICAgfAk5OC0xMDAgfCBUaGUgcGlubmFjbGUgb2YgcXVhbGl0eS4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8U3VwZXJiICAgIHwJOTQtOTcJIHwgQSBncmVhdCBhY2hpZXZlbWVudC4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfEV4Y2VsbGVudCB8CTkwLTkzCSB8IEhpZ2hseSByZWNvbW1lbmRlZC4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnxWZXJ5IEdvb2QgfCAgODctODkJIHwgT2Z0ZW4gZ29vZCB2YWx1ZTsgd2VsbCByZWNvbW1lbmRlZC4gICAgICAgICAgICAgICAgICAgIHwKfEdvb2QJICAgICB8ICA4My04NgkgfCBTdWl0YWJsZSBmb3IgZXZlcnlkYXkgY29uc3VtcHRpb247IG9mdGVuIGdvb2QgdmFsdWUuICAgfAp8QWNjZXB0YWJsZXwJODAtODIJIHwgQ2FuIGJlIGVtcGxveWVkIGluIGNhc3VhbCwgbGVzcy1jcml0aWNhbCBjaXJjdW1zdGFuY2VzIHwKCkFsc28sIHRoaXMgYWxsb3dzIHRvIHRha2UgYSBxdWFudGl0YXRpdmUgZGF0YSBhbmQgdHVybiBpdCBpbnRvIHF1YWxpdGF0aXZlIGRhdGEsIHdoaWNoIGNhbiBiZSB1c2VkIGZvciBkYXRhIHZpc3VhbGl6YXRpb24gbGF0ZXIuIAoKYGBge3J9CiMgZnVuY3Rpb24gdG8gYWRkIHJhdGluZwpyYXRpbmdfY2F0ZWdvcnkgPC0gZnVuY3Rpb24ocG9pbnRzKXsKICBpZihwb2ludHM+PTk4KXsKICAgIHJldHVybigiQ2xhc3NpYyIpCiAgfQogIGVsc2UgaWYgKHBvaW50cz49OTQpewogICAgcmV0dXJuKCJTdXBlcmIiKQogIH0KICBlbHNlIGlmKHBvaW50cz49OTApewogICAgcmV0dXJuKCJFeGNlbGxlbnQiKQogIH0KICBlbHNlIGlmKHBvaW50cz49ODcpewogICAgcmV0dXJuKCJWZXJ5IEdvb2QiKQogIH0KICBlbHNlIGlmKHBvaW50cz49ODMpewogICAgcmV0dXJuKCJHb29kIikKICB9CiAgZWxzZXsKICAgIHJldHVybigiQWNjZXB0YWJsZSIpCiAgfQp9Cgp3aW5lczwtIHdpbmVzICU+JQogIHJvd3dpc2UoKSAlPiUKICBtdXRhdGUocmF0aW5nX2NhdGVnb3J5ID0gcmF0aW5nX2NhdGVnb3J5KHBvaW50cykpCmhlYWQod2luZXMpCmBgYAojIyBDcmVhdGUgYSBSZXZpZXdlciBQcm9maWxlCgojIyMgU3BsaXQgVGFzdGVyIERhdGEKVG8gbWFrZSBvdXIgZGF0YWZyYW1lcyBtb3JlIG1hbmFnYWJsZSB3ZSBzcGxpdCByZWR1bnRhbnQgaW5mb3JtYXRpb24gYWJvdXQgdGhlIHRhc3RlcnMgaW50byBhIG5ldyBkYXRhZnJhbWUuCmBgYHtyfQp0YXN0ZXJzIDwtIHdpbmVzICU+JQogIHNlbGVjdCh0YXN0ZXJfbmFtZSwgdGFzdGVyX3R3aXR0ZXJfaGFuZGxlKSAlPiUgCiAgdW5pcXVlKCkKdGFzdGVycwpgYGAKCgpEcm9wIGB0YXN0ZXJfdHdpdHRlcl9oYW5kbGVgIGluIHdpbmVzIGRhdGFmcmFtZQpgYGB7cn0Kd2luZXMgPC0gd2luZXMgJT4lCiAgc2VsZWN0KC10YXN0ZXJfdHdpdHRlcl9oYW5kbGUpCmhlYWQod2luZXMpCmBgYAoKIyMjIENyZWF0ZSBSZXZpZXdlciBNZXRyaWNzCkVhY2ggcmV2aWV3ZXIgaGFzIHRoZXJlIG93biBiaWFzLiBJbiBvcmRlciB0byBvZmZzZXQgdGhhdCB3ZSBtYWRlIGEgInByb2ZpbGUiIGZvciBlYWNoIHJldmlld2VyLCBUaGlzIHByb2ZpbGUgYWxsb3dzIHVzIHRvIGxhdGVyIG5vcm1hbGl6ZSB0aGUgd2luZSBwb2ludHMgZm9yIG1vcmUgcm9idXN0IGFwcGxlcyB0byBhcHBsZXMgY29tcGFyaXNvbi4gRWFjaCByZXZpZXdlcnMgcHJvZmlsZSBpbmNsdWRlcyBhZGRpdGlvbmFsIGNoYXJhY3RlcmlzdGljcyBsaWtlOiAKCi0gYGF2Z19wb2ludHNgIHdoaWNoIGlzIHRoZSBhdmdlcmFnZSBvZiBhbGwgdGhlIHJldmlld2VyJ3Mgc2NvcmVzCgotIGBzZF9wb2ludHNgIHdoaWNoIGlzIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgYWxsIHRoZSByZXZpZXdlcidzIHNjb3JlcwoKLSBgdmFyX3BvaW50c2Agd2hpY2ggaXMgdGhlIHZhcmlhbmNlIG9mIGFsbCB0aGUgcmV2aWV3ZXIncyBzY29yZXMKCi0gYHJldmlld3NgIHdoaWNoIGlzIHRoZSBudW1iZXIgb2YgcmV2aWV3cyBjb25kdWN0ZWQKCmBgYHtyfQp0YXN0ZXJfcmF0aW5nX3Byb2ZpbGUgPC0gd2luZXMgJT4lCiAgZ3JvdXBfYnkodGFzdGVyX25hbWUpICU+JQogIHN1bW1hcml6ZSgKICAgIGF2Z19wb2ludHMgPSBtZWFuKHBvaW50cyksCiAgICBzZF9wb2ludHMgPSBzZChwb2ludHMpLAogICAgdmFyX3BvaW50cyA9IHZhcihwb2ludHMpLAogICAgcmV2aWV3cyA9IG4oKQogICkKCnRhc3RlcnMgPC0gaW5uZXJfam9pbih0YXN0ZXJzLCB0YXN0ZXJfcmF0aW5nX3Byb2ZpbGUsIGJ5ID0gInRhc3Rlcl9uYW1lIikKaGVhZCh0YXN0ZXJzKQpgYGAKCgojIyBOb3JtYWxpemVkIFBvaW50cwoKU2luY2UsIGVhY2ggcmV2aWV3ZXIgaGFzIGEgZGlmZmVyZW50IGJpYXMgd2UgY3JlYXRlZCBhIG5vcm1hbGl6ZWQgbWV0cmljLCBgbm9ybV9wb2ludHNgLCBieSBsb29raW5nIGF0IHRoZSBudW1iZXIgb2Ygc3RhbmRhcmQgZGV2aWF0aW9pbnMgYSB3aW5lIGlzIGZyb20gdGhlIHJldmlld2VyJ3MgYGF2Z19wb2ludHNgLiBUaGlzIGdpdmVzIHVzZSBhIG1vcmUgYWNjdXJhdGUgcmVwcmVzZW50YXRpb24gb2Ygd2hpY2ggd2hpY2ggd2luZXMgYXJlICJiZXR0ZXIiIHRoYW4gdGhlIHJlc3QuCgpgYGB7cn0Kbm9ybWFsaXplX3BvaW50cyA8LSBmdW5jdGlvbihkYXRhKXsKICBsZWZ0X2pvaW4oZGF0YSwgdGFzdGVycywgYnkgPSAidGFzdGVyX25hbWUiKSU+JQogICAgcm93d2lzZSgpICU+JQogICAgbXV0YXRlKG5vcm1fcG9pbnRzID0gKHBvaW50cy1hdmdfcG9pbnRzKS9zZF9wb2ludHMpICU+JQogICAgc2VsZWN0KC1hdmdfcG9pbnRzLCAtc2RfcG9pbnRzLCAtdmFyX3BvaW50cywgLXRhc3Rlcl90d2l0dGVyX2hhbmRsZSwgLXJldmlld3MpCn0KCndpbmVzIDwtIG5vcm1hbGl6ZV9wb2ludHMod2luZXMpCmhlYWQod2luZXMpIApgYGAKCiMjIERhdGEgU2FuaXRhdGlvbgpIZXJlIHdlIGRvIGhhcmQgY2hlY2tzIHRvIGNsZWFuIGRhdGEgdG8gZW5zdXJlIGludGVncml0eSBvZiBvdXIgZGF0YS4KXApcClZpbnRhZ2Ugc2VlbXMgdG8gaGF2ZSB5ZWFyIDcyMDAsIHNvIHdlIGZpbHRlcmVkIGFsbCBkYXRhIHVwIHRvIDIwMTkKYGBgIHtyfQp3aW5lcyA8LSB3aW5lcyAlPiUKICBmaWx0ZXIodmludGFnZTwyMDE5KQpgYGAKCiMgRGF0YSBFeHBsb3JhdGlvbgpCZWZvcmUsIGNvbmR1Y3RpbmcgYW55IGRldGFpbGVkIGFuYWx5c2lzIG9mIG91ciBkYXRhc2V0LCB3ZSBsb29rZWQgYXQgYSBxdWljayBzdW1tYXJ5IG9mIHRoZSBkYXRhc2V0CmBgYHtyfQpzdW1tYXJ5KHdpbmVzKQpgYGAKCiMjIFVuaXZhcmlhdGUgRXhwbG9yYXRpb24KVG8gYmV0dGVyIHVuZGVyc3RhbmQgdGhlIGRpc3RyaWJ1dGlvbiBvZiBvdXIgZGF0YSB3ZSBkaWQgc29tZSBzaW1wbGUgdW5pdmFyaWF0ZSB2aXN1YWxpemF0aW9uIGJhc2VkIG9uIGNlcnRhaW4gZmllbGRzLiBBZGRpdGlvbmFsbHksIGJlZm9yZSBkb2luZyBhIG11bHRpdmFyaWF0ZSBhbmFseXNpcyBhbmQgYW5zd2VyaW5nIG91ciByZXNlYXJjaCBxdWVzdGlvbnMgd2UgZmlyc3Qgd2FudCB0byBlbnN1cmUgb3VyIGRhdGFzZXQgaXMgcm9idXN0IGFuZCBhbiBhY2N1cmF0ZSByZXByZXNlbnRhdGlvbiBvZiB0aGUgcmVhbCB3b3JsZC4KCiMjIyBBbGNvaG9sIEFtb3VudApUaGUgdmlzdWFsaXphdGlvbiBiZWxvdyBkZXBpY3RzIHRoZSBkaXN0cmlidXRpb24gb2Ygb3VyIGRhdGFzZXQgYmFzZWQgb24gYWxjb2hvbCBwZXJjZW50YWdlLCBgYWxjb2hvbGAgLiBUbyBiZXR0ZXIgdW5kZXJzdGFuZCBhbmQgdmlzdWFsaXplIHRoZSBkYXRhIHdlIGNhdGVnb3JpemVkIHRoZSBncmFwaCBiYXNlZCBvbiBgcmF0aW5nX2NhdGVnb3J5YC4gTm90aWNlLCBhIG1ham9yaXR5IG9mIHdpbmVzIGhhdmUgYW4gYWxjb2hvbCBhbW91bnQgYmV0d2VlbiAxMiUtMTUlIGFuZCBhY2NvcmRpbmcgdG8gW1JlYWwgU2ltcGxlXSgiaHR0cHM6Ly93d3cucmVhbHNpbXBsZS5jb20vaG9saWRheXMtZW50ZXJ0YWluaW5nL2VudGVydGFpbmluZy9mb29kLWRyaW5rL2FsY29ob2wtY29udGVudC13aW5lIikgd2luZSBhbGNob2xpYyBjb250ZW50IGF2ZXJhZ2VzIGJldHdlZW4gMTElLTEzJS4gVGhpcyBsZWFkcyB1cyB0byBiZWxpZXZlIG91ciBkYXRhIGlzIGFuIGFjY3VyYXRlIHJlcHJlc2VudGF0aW9uIG9mIHRoZSByZWFsIHdvcmxkLgpgYGB7cn0Kd2luZXMgJT4lIAogIGdyb3VwX2J5KGFsY29ob2wpICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9oaXN0b2dyYW0oCiAgICBtYXBwaW5nID0gYWVzKAogICAgICB4ID0gYWxjb2hvbCwgCiAgICAgIGZpbGwgPSByYXRpbmdfY2F0ZWdvcnkpLAogICAgbmEucm0gPSBUUlVFLAogICAgYmlucyA9IDUwKSArCiAgc2NhbGVfeF9jb250aW51b3VzKAogICAgYnJlYWtzID0gc2VxKDAsMjUsMSksIAogICAgbGltaXRzID0gYyg0LDIyKSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgQWxjb2hvbCBQZXJjZW50YWdlIiwKICAgIHggPSAiQWxjb2hvbCBQZXJjZW50YWdlIiwKICAgIHkgPSAiQ291bnQiLAogICAgZmlsbCA9ICJSYXRpbmcgQ2F0ZWdvcnkiCiAgKQpgYGAKCiMjIyBWaW50YWdlCk5leHQsIHdlIHdhbnRlZCB0byBzZWUgd2hhdCB2aW50YWdlIG1vc3Qgb2YgdGhlIHdpbmVzIGluIHRoZSBkYXRhc2V0IHdlcmUuIEFnYWluIHRvIGJldHRlciB1bmRlcnN0YW5kIGFuZCB2aXN1YWxpemUgdGhlIGRhdGEgd2UgY2F0ZWdvcml6ZWQgdGhlIGdyYXBoIGJhc2VkIG9uIGByYXRpbmdfY2F0ZWdvcnlgLiBOb3RpY2UsIHRoYXQgdGhlcmUgaXMgcm91Z2hseSwgYW4gZXF1YWwgcGVyY2VudGFnZSBvZiBlYWNoIHJhdGluZyBjYXRlZ29yeSBwZXIgdmludGFnZS4KCihOb3RlOiBEYXRhIHBvaW50cyBiZWZvcmUgMTk5MCBoYXZlIGJlZW4gb21pdHRlZCBmb3IgY2xhcml0eSBpbiB2aXN1YWxpemF0aW9uKQpgYGB7cn0Kd2luZXMgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fYmFyKAogICAgbWFwcGluZyA9IGFlcygKICAgICAgeD12aW50YWdlLCAKICAgICAgZmlsbCA9IHJhdGluZ19jYXRlZ29yeSksCiAgICBuYS5ybSA9IFRSVUUpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoCiAgICBicmVha3MgPSBzZXEoMTk5MCwyMDE5LDUpLCAKICAgIGxpbWl0cyA9IGMoMTk5MCwyMDE5KSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgVmludGFnZSIsCiAgICB4ID0gIlZpbnRhZ2UiLCAKICAgIHkgPSAiQ291bnQiLAogICAgZmlsbCA9ICJSYXRpbmcgQ2F0ZWdvcnkiKQpgYGAKCiMjIyBXaW5lcnkKVG8gYmV0dGVyIHVuZGVyc3RhbmQgdGhlIG51bWJlciB3aW5lcyBwZXIgd2luZXJ5LCB3ZSBkaWQgYSB2aXN1YWxpemF0aW9uIHRoYXQgY291bnRzIHRoZSBudW1iZXIgb2Ygd2luZXMgcGVyIHdpbmVyeSBzaG93aW5nIG9ubHkgVG9wIDEwIHdpbmVyeXMgdG8gZ2l2ZSB5b3UgYW4gaWRlYSB3aGF0IHdpbmVyeSBoYXMgdGhlIG1vc3Qgc2VsY3Rpb24gb2Ygd2luZXMuIE5vdGljZSwgZWFjaCBvZiB0aGUgdG9wIDEwIHByb2R1Y2VycyBvZiB3aW5lIGhhdmUgb3ZlciAxMDAgZGlmZmVyZW50IHdpbmUgbGFiZWxzLgpgYGB7cn0Kd2luZXMgJT4lCiAgZ3JvdXBfYnkod2luZXJ5KSAlPiUKICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQogIGFycmFuZ2UoZGVzYyhjb3VudCkpICU+JQogIHNsaWNlKDE6MTApICU+JQogIGdncGxvdCgpICsKICBnZW9tX2NvbCgKICAgIG1hcHBpbmcgPSBhZXMoCiAgICAgIHg9IHJlb3JkZXIod2luZXJ5LCBjb3VudCksCiAgICAgIHkgPSBjb3VudCwKICAgICAgZmlsbCA9IHdpbmVyeSkpICsKICBsYWJzKAogICAgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIFdpbmVyeSAoVG9wIDEwKSIsCiAgICB4ID0gIldpbmVyeSIsIAogICAgeSA9ICJDb3VudCIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBjb29yZF9mbGlwKCkKYGBgCgojIyMgUHJvdmluY2UKVG8gYmV0dGVyIHVuZGVyc3RhbmQgdGhlIG51bWJlciB3aW5lcyBwZXIgcHJvdmluY2UsIHdlIGRpZCBhIHZpc3VhbGl6YXRpb24gdGhhdCBjb3VudHMgdGhlIG51bWJlciBvZiB3aW5lcyBwZXIgcHJvdmluY2Ugc2hvd2luZyBvbmx5IHRoZSB0b3AgMTAgcHJvdmluY2VzIHdpdGggdGhlIG1vc3Qgd2luZXMuIFRoaXMgY2FuIGdpdmUgdGhlIHJlYWRlciBhbiBpZGVhIHdoZXJlIHRoZWlyIHdpbmUgd2lsbCBtb3N0IGxpa2VseSBiZSBtYWRlIHdpdGggQ2FsaWZvcm5pYSBzdGFuZGluZyBvdXQgYXMgYSBjbGVhciBsZWFkZXIuCmBgYHtyfQp3aW5lcyAlPiUgCiAgZ3JvdXBfYnkocHJvdmluY2UpICU+JSAKICBzdW1tYXJpemUoY291bnQgPSBuKCkpICU+JSAKICBhcnJhbmdlKGRlc2MoY291bnQpKSAlPiUgCiAgc2xpY2UoMToxMCkgJT4lIAogIGdncGxvdCgpKwogIGdlb21fY29sKAogICAgbWFwcGluZyA9IGFlcygKICAgICAgeCA9IHJlb3JkZXIocHJvdmluY2UsIGNvdW50KSwgCiAgICAgIHkgPSBjb3VudCwKICAgICAgZmlsbCA9IHByb3ZpbmNlKSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgUHJvdmluY2UgKFRvcCAxMCkiLAogICAgeCA9ICJQcm92aW5jZSIsIAogICAgeSA9ICJDb3VudCIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBjb29yZF9mbGlwKCkKYGBgCiMjIyBXaW5lIENhdGVnb3J5Ck5leHQsIHdlIHdhbnRlZCB0byB2aXN1YWxpemUgdGggZGlzdHJpYnV0aW9uIG9mIGRpZmZlcmVudCB3aW5lIGNhdGVnb3JpZXMgaW4gb3VyIGRhdGFzZXQuVG8gYmV0dGVyIHVuZGVyc3RhbmQgYW5kIHZpc3VhbGl6ZSB0aGUgZGF0YSB3ZSBjYXRlZ29yaXplZCB0aGUgZ3JhcGggYmFzZWQgb24gYHJhdGluZ19jYXRlZ29yeWAuIE5vdGljZSwgYSBtYWpvcml0eSBvZiB0aGUgd2luZXMgYXJlIHJlZCBvciB3aGl0ZSB3aW5lcwoKYGBge3J9CndpbmVzICU+JSAKICBnZ3Bsb3QoKSsKICBnZW9tX2JhcigKICAgIG1hcHBpbmcgPSBhZXMoCiAgICAgIHggPSBjYXRlZ29yeSwKICAgICAgZmlsbCA9IHJhdGluZ19jYXRlZ29yeSkpICsKICBsYWJzKAogICAgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIFdpbmUgQ2F0ZWdvcnkiLAogICAgeCA9ICJXaW5lIENhdGVnb3J5IiwgCiAgICB5ID0gIkNvdW50IiwKICAgIGZpbGwgPSAiUmF0aW5nIENhdGVnb3J5IikgKwogIGNvb3JkX2ZsaXAoKQpgYGAKCiMjIyBQcmljZQpOZXh0LCB3ZSB3YW50ZWQgdG8gdmlzdWFsaXplIHRoZSBkaXN0cmlidXRpb24gb2YgcHJpY2UgaW4gb3VyIGRhdGFzZXQuIFRvIGJldHRlciB1bmRlcnN0YW5kIGFuZCB2aXN1YWxpemUgdGhlIGRhdGEgd2UgY2F0ZWdvcml6ZWQgdGhlIGdyYXBoIGJhc2VkIG9uIGByYXRpbmdfY2F0ZWdvcnlgLiBOb3RpY2UsIGEgbWFqb3JpdHkgb2Ygd2luZXMgYXJlIFwkNTAgYW5kIGJlbG93IHdpdGggdGhlIG1vc3QgY29tbW9uIGJlaW5nIGJldHdlZW4gXCQxMiAtIFwkMjUuIEFnYWluLCB0aGlzIGFjY3VyYXRlbHkgcmVwcmVzZW50cyB0aGUgcmVhbCB3b3JsZCBhcyBzdGF0ZWQgYnkgW1Zpdmlub10oaHR0cHM6Ly93d3cudml2aW5vLmNvbS93aW5lLW5ld3MvaG93LW11Y2gtZG9lcy1hLWdvb2QtYm90dGxlLW9mLXdpbmUtY29zdCkgdGhlIGF2ZXJhZ2UgcHJpY2UgZm9yIGdvb2QgYm90dGxlIG9mIHJlZC93aGl0ZSB3aW5lIGlzIH5cJDE1IGFuZCB+XCQyOCBmb3IgYSB2ZXJ5IGdvb2QgYm90dGxlLiAoQ0FVVElPTjogVGhlIFZpdmlubyBwcmljZXMgZGVub3RlZCB3ZXJlIHNpbXBseSBhbiBhdmVyYWdlIGZvciByZWQvd2hpdGUgd2luZXMgYXZlcmFnZSBjb3N0cy4gVGhpcyB3YXMgZG9uZSB0byBzaW1wbHkgZ2VuZXJhbGl6ZSB0aGUgaW5mb3JtYXRpb24gdG8gZG8gYSBzaW1wbGUgY29tcGFyc2lvbi4gQWxzbywgdGhpcyBsaW1pdGVkIHRvIHJlZC93aGl0ZSB3aW5lIGFuZCBkb2VzIG5vdCBhY2N1cmF0ZWx5IGluY2x1ZGUgb3RoZXIgdHlwZXMpCgooTm90ZTogRGF0YSBwb2ludHMgYWJvdmUgJDQwMCBoYXZlIGJlZW4gb21pdHRlZCBmb3IgY2xhcml0eSBpbiB2aXN1YWxpemF0aW9uKQpgYGB7cn0Kd2luZXMgJT4lIAogIGZpbHRlcihwcmljZSA8IDQwMCkgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2hpc3RvZ3JhbSgKICAgIG1hcHBpbmcgPSBhZXMoCiAgICAgIHg9cHJpY2UsIAogICAgICBmaWxsID0gcmF0aW5nX2NhdGVnb3J5KSwKICAgIGJpbndpZHRoID0gMTUpICsKICBsYWJzKAogICAgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIFByaWNlIiwKICAgIHggPSAiUHJpY2UiLCAKICAgIHkgPSAiQ291bnQiLAogICAgZmlsbCA9ICJSYXRpbmcgQ2F0ZWdvcnkiKQpgYGAKCiMjIyAgUG9pbnRzIApOZXh0LCB3ZSB3YW50ZWQgdG8gdmlzdWFsaXplIHRoZSBkaXN0cmlidXRpb24gb2YgcG9pbnRzIGluIG91ciBkYXRhc2V0LiBOb3RpY2UsIGhlcmUgdGhhdCBhIG1ham9yaXR5IG9mIHdpbmVzIHJlY2lldmUgYSBzY29yZSBiZXR3ZWVuIDg3IGFuZCA5MC4gV2hpY2ggaXMgYWNjdXJhdGUgdG8gdGhlIGluZm9ybWF0aW9uIHByb3ZpZGVkIG9uIFtXaW5lIFNlYXJjaGVyXSgiaHR0cHM6Ly93d3cud2luZS1zZWFyY2hlci5jb20vY3JpdGljcy0xNy13aW5lK2VudGh1c2lhc3QiKSB3aGljaCBzdGF0ZXMgNTAlIG9mIHRoZSBzY29yZXMgZmFsbCBiZXR3ZWVuIDg2LTkwIHBvaW50IGZyb20gV2luZSBFbnRodXNpYXN0IHJhdGluZ3MuCgooTm90ZTogV2Ugb3VyIGRhdGFzZXQgd2FzIHJldGVyaXZlZCBmcm9tIHRoZSBXaW5lIEVudGh1c2lhc3Qgd2Vic2l0ZSkKYGBge3J9CndpbmVzICU+JQogIGdncGxvdCgpICsKICBnZW9tX2hpc3RvZ3JhbSgKICAgIG1hcHBpbmcgPSBhZXMoeD1wb2ludHMpLAogICAgYmlucyA9IDIwKSsKICBsYWJzKAogICAgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIFBvaW50cyIsCiAgICB4ID0gIlBvaW50cyIsIAogICAgeSA9ICJDb3VudCIpCmBgYAoKIyMgTXVsdGl2YXJpYXRlIEV4cGxvcmF0aW9uCk5vdywgdGhhdCB3ZSBoYXZlIGEgYmV0dGVyIHVuZGVyc3RhbmRpbmcgb2Ygb3VyIGRhdGEgYW5kIHdlIGtub3cgaXQgaXMgYSBhY2N1YXJlIHJlcHJlc2VudGF0aW9uIG9mIHRoZSByZWFsIHdvcmxkIHdlIGNhbiBwcmVmb3JtIGFuIG1vcmUgZGV0YWlsZWQgYW5heWxzaXMgdXNpbmcgbXVsdGlwbGUgdmFyaWFibGVzLgoKIyMjIFBvaW50cyBieSBUYXN0ZXIKVG8gdW5kZXJzdGFuZCB0aGUgcG9pbnQgZGlzdHJpYnV0aW9uIGJ5IHRhc3RlcnMsIHdlIGRpZCBhIG11bHRpdmFyaXRlIHZpc3VhbGl6YXRpb24gdGhhdCBjb29yZWxhdGVzIHRhc3RlciBuYW1lcyBiYXNlZCBvbiB0aGUgYXZlcmFnZSB3aW5lIHBvaW50cyBhcyBpZGVudGlmaWVkICBieSB0aGUgeC1pbnRlcmNlcHQuIFRoaXMgZ2l2ZXMgdGhlIHJlYWRlciBhbiBpZGVhIG9mIGhvdyBzb21lIHJldmlld2VycyBjb3JyZWxhdGUgdG8gdGhlIG92ZXJhbGwgYXZlcmFnZS4KCihOb3RlOiBUaGUgImJsYW5rIiByZXByZXNlbnRzIHVua25vd24gcmV2aWV3ZXJzLiBXZSBhc3N1bWVkIHRoZSByZXZpZXdlcnMgbm90IG5hbWVkIGhhdmUgbm90IHJhdGVkIGEgc2lnbmZpY2FudCBhbW91bnQgb2Ygd2luZXMgYW5kIGNhbiBiZSBncm91cGVkIGludG8gYSBzaW5ndWxhciByZXZpZXdlcikKYGBge3J9CndpbmVzICU+JQogIGdncGxvdCgpICsKICBnZW9tX2JveHBsb3QoCiAgICBtYXBwaW5nID0gYWVzKAogICAgICB4PXRhc3Rlcl9uYW1lLAogICAgICB5PXBvaW50cywgCiAgICAgIGNvbG9yID0gdGFzdGVyX25hbWUpKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gbWVhbih3aW5lcyRwb2ludHMpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSsKICBsYWJzKAogICAgdGl0bGUgPSAiUG9pbnRzIGJ5IFRhc3RlciIsCiAgICB4PSJUYXN0ZXIgTmFtZSIsCiAgICB5PSJQb2ludHMiCiAgKSsKICBjb29yZF9mbGlwKCkKYGBgCgoKIyMjIFByaWNlIGJ5IFBvaW50cwpUbyB1bmRlcnN0YW5kIHRoZSBwcmljZSBkaXN0cmlidXRpb24gYnkgcG9pbnRzLCB3ZSBkaWQgYSBtdWx0aXZhcml0ZSB2aXN1YWxpemF0aW9uIHRoYXQgY3JlYXRlcyBhIHNjYXR0ZXIgcGxvdCBvZiB0aGUgd2luZXMgYmFzZWQgb24gcG9pbnRzIGFuZCBwcmljZS4gVGhlbiwgd2UgYWRkZWQgYSBzbW9vdGggdHJhbnNmb3JtYXRpb24gb24gaWRlbnRpZnkgdHJlbmRzLiBOb3RpY2UsIHRoZSBkYXRhIGlzICJzdGFja2VkIiBhbmQgdGhlIHNvY3JlcyByYW5nZSBmcm9tIDgwLTEwMApgYGB7cn0Kd2luZXMgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX3BvaW50KAogICAgbWFwcGluZyA9IGFlcygKICAgICAgeCA9IHBvaW50cywgCiAgICAgIHkgPSBwcmljZSwgCiAgICAgIGNvbG9yID0gY2F0ZWdvcnkpLAogICAgbmEucm0gPSBUUlVFLAogICAgYWxwaGEgPSAwLjIpICsKICBsYWJzKAogICAgdGl0bGUgPSAiUHJpY2UgYnkgUG9pbnRzIiwgCiAgICB4ID0gIlBvaW50cyIsCiAgICB5ID0gIlByaWNlIiwKICAgIGNvbG9yID0gIldpbmUgQ2F0ZWdvcnkiKSArCiAgZ2VvbV9zbW9vdGgoCiAgICBtYXBwaW5nID0gYWVzKAogICAgICB4ID0gcG9pbnRzLCAKICAgICAgeSA9IHByaWNlKSwKICAgIG5hLnJtID0gVFJVRSkKYGBgCgpTaW5jZSwgdGhlcmUgYXJlIG11bHRpcGxlIG91dGxpZXJzIGFuZCB0aGUgdmlzdWFsaXphdGlvbiBpcyBjbHVzdGVyZWQuIEJ5IHByZWZvcm1pbmcgYSBsb2cgb24gYWxsIHRoZSBwcmljZXMgd2UgY2FuIHJlZHVjZSB0aGUgc2tld25lc3Mgb2YgdGhlIHZpc3VhbGl6YXRpb24uIE5vdGljZSwgYXMgcXVhbGl0eSBvZiB3aW5lIGluY3JlYXNlcyBwcmljZSBpbmNyZWFzZXMgZXhwb25lbnRpYWxseS4KYGBge3J9CndpbmVzICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludCgKICAgIG1hcHBpbmcgPSBhZXMoCiAgICAgIHggPSBwb2ludHMsIAogICAgICB5ID0gbG9nKHByaWNlKSwgCiAgICAgIGNvbG9yID0gY2F0ZWdvcnkpLAogICAgbmEucm0gPSBUUlVFLAogICAgYWxwaGEgPSAwLjIpICsKICBsYWJzKAogICAgdGl0bGUgPSAibG9nKFByaWNlKSBieSBQb2ludHMiLCAKICAgIHggPSAiUG9pbnRzIiwKICAgIHkgPSAibG9nKFByaWNlKSIsCiAgICBjb2xvciA9ICJXaW5lIENhdGVnb3J5IikgKwogIGdlb21fc21vb3RoKAogICAgbWFwcGluZyA9IGFlcygKICAgICAgeCA9IHBvaW50cywgCiAgICAgIHkgPSBsb2cocHJpY2UpKSwKICAgIG5hLnJtID0gVFJVRSkKYGBgCgojIyMjIEdyb3VwIGJ5IFdpbmUgQ2F0ZWdvcnkKTmV4dCwgb3VyIGdyb3VwIHdhbnRlZCB0byBkbyBhIG1vcmUgZ3JhbnVsYXIgYW5hbHlzaXMgYnkgbG9va2luZyBhdCBob3cgdGhlIHByaWNlIHZhcmllcyBieSBwb2ludHMgZ3JvdXBlZCBieSB3aW5lIGNhdGVnb3J5LiBOb3RpY2UsIGFsbCB0aGUgcHJpY2UgZ28gdXAgYXMgcG9pbnRzIGdvIHVwLCBidXQgdGhlIGdyb3d0aCByYXRlcyBhcmUgZGlmZmVyZW50IHBlciB3aW5lIGNhdGVnb3J5LgpgYGB7cn0Kd2luZXMgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX3BvaW50KAogICAgbWFwcGluZyA9IGFlcygKICAgICAgeCA9IHBvaW50cywgCiAgICAgIHkgPSBsb2cocHJpY2UpLCAKICAgICAgY29sb3IgPSBjYXRlZ29yeSksCiAgICBhbHBoYSA9MC4yLAogICAgbmEucm0gPSBUUlVFKSArCiAgZ2VvbV9zbW9vdGgoCiAgICBtYXBwaW5nID0gYWVzKAogICAgICB4ID0gcG9pbnRzLCAKICAgICAgeSA9IGxvZyhwcmljZSkpLAogICAgbmEucm0gPSBUUlVFKSArCiAgZmFjZXRfd3JhcCh+Y2F0ZWdvcnkpICsKICBsYWJzKAogICAgdGl0bGUgPSAibG9nKFByaWNlKSBieSBQb2ludHMiLCAKICAgIHggPSAiUG9pbnRzIiwKICAgIHkgPSAibG9nKFByaWNlKSIpKwogIHRoZW1lX21pbmltYWwoKSsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIgKQpgYGAKCgojIERhdGEgQW5hbHlzaXMKTm93IHRoYXQgd2UgZnVsbHkgdW5kZXJzdGFuZCB0aGUgZGF0YXNldCB3ZSBhcmUgd29ya2luZyB3aXRoLiBXZSBwbGFuIHRvIGFuc3dlciBzb21lIHJlc2VhcmNoIHF1ZXN0aW9ucyBwcm9wb3NlZCBieSB0aGUgdGVhbS4KCiMjIFdoYXQgaXMgdGhlIGJlc3Qgd2luZT8KQSBlYXN5IHdheSB0byBkZXRlcm1pbmUgdGhlIGJlc3Qgd2luZSBpcyBieSBzaW1wbHkgZmluZGluZyB0aGUgdG9wIDEwIHdpbmVzLgpgYGB7cn0Kd2luZXMgJT4lCiAgYXJyYW5nZShkZXNjKHBvaW50cykpICU+JQogIHNsaWNlKDE6MTApICU+JQogIHNlbGVjdCh0aXRsZSxwcmljZSwgcG9pbnRzLHJhdGluZ19jYXRlZ29yeSwgbm9ybV9wb2ludHMpCmBgYAoKSG93ZXZlciwgdGhpcyBkb2VzIG5vdCBhY2NvdW50IGZvciB0aGUgdGFzdGVycyBiaWFzLiBJbnN0ZWFkLCBvdXIgZ3JvdXAgIm5vcm1hbGl6ZWQiIHRoZSBwb2ludHMgYmFzZWQgb24gZWFjaCB0YXN0ZXIgYmFzZWQgb24gdGhlIG51bWJlciBvZiBzdGFuZGFyZCBkZXZpYXRpb25zIGFuIHdpbmVzIGlzIGZyb20gdGhlIHJhdGVycyBhdmVyYWdlLiBGb3IgZXhhbXBsZSwgVGFzdGVyIEEgY291bGQgZ2l2ZSBhIHdpbmUgMTAwIGJ1dCBoYXMgYW4gYXZnZXJhZ2UgcmF0aW5nIHNjb3JlIG9mIDk1IHdpdGggYSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgNS4gV2hlcmVhcywgVGFzdGVyIEIgY291bGQgZ2l2ZSBhIHdpbmUgOTEgYW5kIGhhdmUgYW4gYXZlcmFnZSBzY29yZSBvZiA4NyB3aXRoIGEgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIDIuIEFsdGhvdWdoLCB0aGUgd2luZSB0YXN0ZWQgYnkgVGFzdGVyIEEgZ290IGEgcGVyZmVjdCAxMDAgc2NvcmUsIFRhc3RlciBCJ3Mgd2luZSB3YXMgbXVjaCAiYmV0dGVyIiB3aW5lIHNpbmNlIGl0IHdhcyAyIHN0YW5kYXJkIGRldmlhdGlvbnMgZnJvbSB0aGUgdGFzdGVycyBhdmdlcmFnZSBjb21wYXJlZCB0byAxIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgb3RoZXIgd2luZS4KCkxvb2tpbmcgYXQgdGhlIGBub3JtX3BvaW50c2AgdGhlc2UgYXJlIHRoZSB0b3AgMTAgYmVzdCB3aW5lcwpgYGB7cn0Kd2luZXMgJT4lCiAgYXJyYW5nZShkZXNjKG5vcm1fcG9pbnRzKSkgJT4lCiAgc2xpY2UoMToxMCklPiUKICBzZWxlY3QodGl0bGUscHJpY2UsIHBvaW50cyxyYXRpbmdfY2F0ZWdvcnksIG5vcm1fcG9pbnRzKQpgYGAKCiMjIFdoYXQgaXMgdGhlIGJlc3QgdmFsdWUgd2luZT8KQSBzaW1wbGUgdmFsdWUgbWV0cmljIHdlIGNhbiB1c2UgdG8gZGV0ZXJtaW5lIGJlc3QgdmFsdWUgaXMgYHBvaW50cy9wcmljZWAKYGBge3J9CndpbmVzICU+JQogIGFycmFuZ2UoZGVzYyhwb2ludHMvcHJpY2UpKSAlPiUKICBzbGljZSgxOjEwKSU+JQogIHNlbGVjdCh0aXRsZSxwcmljZSwgcG9pbnRzLHJhdGluZ19jYXRlZ29yeSwgbm9ybV9wb2ludHMpCmBgYAoKSG93ZXZlciwgYWdhaW4gdGhpcyBtZXRyaWMgaXMgbm90IG5vcm1hbGl6ZWQuIEluc3RlYWQsIGBub3JtX3BvaW50cy9wcmljZWAgd291bGQgeWllbGQgbW9yZSByb2J1c3QgcmVzdWx0cy4KYGBge3J9CndpbmVzICU+JQogIGFycmFuZ2UoZGVzYyhub3JtX3BvaW50cy9wcmljZSkpICU+JQogIHNsaWNlKDE6MTApICU+JQogIHNlbGVjdCh0aXRsZSxwcmljZSwgcG9pbnRzLHJhdGluZ19jYXRlZ29yeSwgbm9ybV9wb2ludHMpCmBgYAoKIyMgV2hpY2ggcHJvdmluY2UgaGFzIHRoZSBiZXN0IHdpbmU/ClRvIGRldGVybWluZSB0aGUgYmVzdCBwcm92aW5jZSBmb3Igd2luZSBieSBwb2ludHMsIHdlIGF2ZXJhZ2VkIHRoZSBwb2ludHMgb2YgYWxsIHdpbmVzIHBlciBwcm92aW5jZSB3aXRoIGEgc2FtcGxlIHNpemUgZ3JlYXRlciB0aGFuIDMwIGFuZCByZXR1cm5lZCB0aGUgdG9wIDEwIHdpdGggc3RhbmRhcmQgZXJyb3IuIE5vdGljZSwgaG93IHRoZSBzdGFuZGFyZCBlcnJvciBpcyBsb3cgbWVuYWluZyB0aGUgc3ByZWFkIG9mIG91ciBkYXRhIGlzIGFsc28gbG93IGFuZCB0aGUgYXZlcmFnZSBpcyB2ZXJ5IGFjY3VyYXRlLgoKYGBge3J9CndpbmVzICU+JSAKICBncm91cF9ieShwcm92aW5jZSkgJT4lCiAgc3VtbWFyaXNlKAogICAgYXZnX3BvaW50c19wcm92ID0gbWVhbihwb2ludHMpLCAKICAgIGNvdW50ID0gbigpLCAKICAgIHN0ZF9lcnJfcG9pbnRzX3Byb3YgPSBzZChwb2ludHMpL3NxcnQoY291bnQpKSAlPiUKICBmaWx0ZXIoY291bnQ+MzApICU+JQogIGFycmFuZ2UoZGVzYyhhdmdfcG9pbnRzX3Byb3YpKSAlPiUKICBzbGljZSgxOjEwKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9jb2woCiAgICBtYXBwaW5nID0gYWVzKAogICAgICB4ID0gcmVvcmRlcihwcm92aW5jZSxhdmdfcG9pbnRzX3Byb3YpLCAKICAgICAgeSA9IGF2Z19wb2ludHNfcHJvdiwKICAgICAgZmlsbCA9IHByb3ZpbmNlKSkgKwogICBnZW9tX2Vycm9yYmFyKAogICAgbWFwcGluZyA9IGFlcygKICAgICAgeCA9IHByb3ZpbmNlLAogICAgICB5ID0gYXZnX3BvaW50c19wcm92LAogICAgICB5bWluID0gYXZnX3BvaW50c19wcm92IC0gc3RkX2Vycl9wb2ludHNfcHJvdiwgCiAgICAgIHltYXggPSBhdmdfcG9pbnRzX3Byb3YgKyBzdGRfZXJyX3BvaW50c19wcm92CiAgICAgICksCiAgICB3aWR0aCA9IDAuMikrCiAgc2NhbGVfeV9jb250aW51b3VzKAogICAgbGltaXRzPWMoODUsOTUpLCAKICAgIG9vYiA9IHJlc2NhbGVfbm9uZSkrCiAgbGFicygKICAgICAgeCA9ICdQcm92aW5jZScsIAogICAgICB5ID0gIkF2ZXJhZ2UgUG9pbnRzIiwgCiAgICAgIHRpdGxlID0gIkF2ZXJhZ2UgUG9pbnRzIEJ5IFByb3ZpbmNlIChUb3AgMTApIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikrCiAgY29vcmRfZmxpcCgpCmBgYAoKIyMgV2hpY2ggd2luZSB2YXJpZXR5IGlzIHRoZSBiZXN0PwpUbyBkZXRlcm1pbmUgdGhlIGJlc3QgdmFyaWV0eSBvZiB3aW5lIHdlIHVzZSB0aGUgYXZlcmFnZSBwb2ludCBvZiBhbGwgd2luZXMgcGVyIHZhcmlldHkgd2l0aCBhIHNhbXBsZSBzaXplIGdyZWF0ZXIgdGhhbiAzMC4gVGhlIGdyYXBoIGJlbG93IHNob3dzIHRoZSB0aGUgdG9wIDEwIHZhcmlldGllcyB3aXRoIHRoZWlyIHJlc3BlY3RpdmUgc3RhbmRhcmQgZXJyb3IuCgpgYGB7cn0Kd2luZXMgJT4lIAogIGdyb3VwX2J5KHZhcmlldHkpICU+JQogIHN1bW1hcmlzZSgKICAgIGF2Z19wb2ludHNfdmFyaWV0eSA9IG1lYW4ocG9pbnRzKSwgCiAgICBjb3VudCA9IG4oKSwgCiAgICBzdGRfZXJyX3BvaW50c192YXJpZXR5ID0gc2QocG9pbnRzKS9zcXJ0KGNvdW50KSkgJT4lCiAgZmlsdGVyKGNvdW50PjMwKSAlPiUKICBhcnJhbmdlKGRlc2MoYXZnX3BvaW50c192YXJpZXR5KSkgJT4lCiAgc2xpY2UoMToxMCkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fY29sKAogICAgbWFwcGluZyA9IGFlcygKICAgICAgeCA9IHJlb3JkZXIodmFyaWV0eSxhdmdfcG9pbnRzX3ZhcmlldHkpLCAKICAgICAgeSA9IGF2Z19wb2ludHNfdmFyaWV0eSwKICAgICAgZmlsbCA9IHZhcmlldHkpKSArCiAgIGdlb21fZXJyb3JiYXIoCiAgICBtYXBwaW5nID0gYWVzKAogICAgICB4ID0gdmFyaWV0eSwKICAgICAgeSA9IGF2Z19wb2ludHNfdmFyaWV0eSwKICAgICAgeW1pbiA9IGF2Z19wb2ludHNfdmFyaWV0eSAtIHN0ZF9lcnJfcG9pbnRzX3ZhcmlldHksIAogICAgICB5bWF4ID0gYXZnX3BvaW50c192YXJpZXR5ICsgc3RkX2Vycl9wb2ludHNfdmFyaWV0eQogICAgICApLAogICAgd2lkdGggPSAwLjIpKwogIHNjYWxlX3lfY29udGludW91cygKICAgIGxpbWl0cz1jKDg1LDk1KSwgCiAgICBvb2IgPSByZXNjYWxlX25vbmUpKwogIGxhYnMoCiAgICAgIHggPSAnVmFyaWV0eScsIAogICAgICB5ID0gIkF2ZXJhZ2UgUG9pbnRzIiwgCiAgICAgIHRpdGxlID0gIkF2ZXJhZ2UgUG9pbnRzIEJ5IFZhcmlldHkgKFRvcCAxMCkiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSsKICBjb29yZF9mbGlwKCkKYGBgCgojIyBKZXNzIGZyb20gTmV3IEdpcmzigJlzIGZhdm9yaXRlIHdpbmU/ClNvdW5kcyBsaWtlIGEgc2lsbHkgcXVlc3Rpb24sIGJ1dCB0YWtlIGEgY2xvc2VyIGxvb2sgYW5kIHlvdSdsbCBmaW5kIGFuIGludGVyZXN0aW5nIHF1ZXN0aW9uIHdpdGhpbiBpdDogIkNhbiB3ZSBkZXRlcm1pbmUgdGhlIGJlc3QgdmFsdWUgd2luZSBiYXNlZCBvbiBob3cgbXVjaCBwZW9wbGUgYXJlIHdpbGxpbmcgdG8gcGF5PyIgIFdFIFNVUkUgQ0FOIQoKYGBge3J9CiMgSWYgeW91IHdhbnQgdG8gZ2V0IHVzZXIgaW5wdXQKI3VzZXJfcHJpY2UgPC0gcmVhZGxpbmUocHJvbXB0ID0gIkhvdyBtdWNoIGFyZSB5b3Ugd2lsbGluZyB0byBzcGVuZCBvbiBhIGJvdHRsZT8iKQojdXNlcl9wcmljZSA8LSBhcy5pbnRlZ2VyKHVzZXJfcHJpY2UpCnVzZXJfcHJpY2U8LTExCgp3aW5lcyAlPiUgCiAgZmlsdGVyKHByaWNlIDw9IHVzZXJfcHJpY2UpICU+JQogIGFycmFuZ2UoZGVzYyhub3JtX3BvaW50cy9wcmljZSkpICU+JQogIHNsaWNlKDE6MTApICU+JQogIHNlbGVjdCh0aXRsZSwgcHJpY2UsIHBvaW50cyxyYXRpbmdfY2F0ZWdvcnksIG5vcm1fcG9pbnRzKQpgYGAKCk5vdyBiYWNrIHRvIHRoZSBvcmlnbmFsIHF1ZXN0aW9uIHdpdGggSmVzcwo8YnI+CjxkaXYgc3R5bGU9IndpZHRoOjEwMCU7aGVpZ2h0OjA7cGFkZGluZy1ib3R0b206NjElO3Bvc2l0aW9uOnJlbGF0aXZlOyI+PGlmcmFtZSBzcmM9Imh0dHBzOi8vZ2lwaHkuY29tL2VtYmVkLzNvc3hZM0p1NnAyakpiQm84OCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgc3R5bGU9InBvc2l0aW9uOmFic29sdXRlIiBmcmFtZUJvcmRlcj0iMCIgY2xhc3M9ImdpcGh5LWVtYmVkIiBhbGxvd0Z1bGxTY3JlZW4+PC9pZnJhbWU+PC9kaXY+CgpgYGB7cn0gCndpbmVzICU+JSAKICBmaWx0ZXIocHJpY2UgPCAxMSkgJT4lCiAgZmlsdGVyKGNhdGVnb3J5ID09ICJTcGFya2xpbmciKSAlPiUKICBmaWx0ZXIoZ3JlcGwoInBpbmsiLCB0aXRsZSwgaWdub3JlLmNhc2UgPSBUKSA9PSBUKSAlPiUKICBzZWxlY3QodGl0bGUscHJpY2UsIHBvaW50cyxyYXRpbmdfY2F0ZWdvcnksIG5vcm1fcG9pbnRzKQpgYGAKQXMgeW91IGNhbiBzZWUsICJZZWxsb3cgVGFpbCAyMDE1IFBpbmsgQnViYmxlcyBTcGFya2xpbmcgKFNvdXRoIEF1c3RyYWxpYSkiIGlzIEplc3MncyB0eXBlIG9mIHdpbmUhCgojIENvbmNsdXNpb24KCiMgRnV0dXJlIFdvcmtzCgojIFdvcmtzIENpdGVkCg==